#pragma once
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstdlib>
#include "codec.cpp"

using std::cout;
using std::endl;
using std::fstream;
using std::ios;
using std::make_pair;

vector<pair<char, int> > getWeight_de(string &);
void decode_file(string &, string &, HuffmanTree &);
bool readBit(fstream &);

//解码主程序
void decode_main(string infname, string oufname)
{
	vector<pair<char, int> > weight;
	vector<pair<char, int> >::iterator weight_p;
	vector<inf> dict;
	vector<inf>::iterator dict_p;
	HuffmanTree h_tree;
	
	weight = getWeight_de(infname);									//从编码文件里读取原文件里各个字符出现的次数

	// heapSort(weight);											//文件中的字典是有序的，所以无需将所有字符按权值排序

	h_tree = BuildHuffmanTree(weight);								//生成哈夫曼树
	h_tree.CreateDict(dict);

	for(dict_p = dict.begin(); dict_p != dict.end(); dict_p++)		//在屏幕上输出编码
	{
		string str;
		char ch = dict_p->ch;
		if(ch == '\n')
			str = "(LF)";
		else if(ch == '\t')
			str = "(TAB)";
		else if(ch == ' ')
			str = "(SPACE)";
		else
			str = ch;
		cout << str << "\t" << dict_p->weight << "\t" << dict_p->code << endl;
	}

	decode_file(infname, oufname, h_tree);							//将编码文件解码后写入文本文件

	return;
}

//从编码文件中获取原文件中各个字符出现的次数
vector<pair<char, int> > getWeight_de(string &infname)
{
	vector<pair<char, int> > weight;
	vector<pair<char, int> >::iterator p, t;
	int spenum;
	char ch;
	int we;

	fstream fin(infname.c_str(), ios::in|ios::binary);				//打开编码文件
	if(fin.fail())
	{
		cout << "Cannot open the input file." << endl;
		exit(0);
	}

	fin.seekg(sizeof(int), ios::beg);
	fin.read((char *)&spenum, sizeof(int));							//获取文本文件中出现的字符种类数

	for(int i = 1; i <= spenum; i++)
	{
		fin.read((char *)&ch, sizeof(char));
		fin.read((char *)&we, sizeof(int));
		weight.push_back(make_pair(ch, we));						//获取文本文件中各个字符出现的次数
	}

	fin.close();
	return weight;
}

//按位读文件函数
bool readBit(fstream &fp)
{
	static char buffer = 0;
	static int offset = -1;
	bool b;

	if(offset == -1 && !fp.eof())									//buffer为空，则buffer中读取一个字节
	{
		if(!fp.eof())
			fp.read(&buffer, 1);
			offset = 7;
	}

	b = (buffer&(1<<offset))>>offset;								//取返回buffer的高位并返回
	offset--;
	return b;
}



//文件解码函数
void decode_file(string &infname, string &oufname, HuffmanTree &h_tree)
{
	fstream fin(infname.c_str(), ios::in|ios::binary);
	fstream fout(oufname.c_str(), ios::out);
	HuffmanNode *root, *cur;										//哈夫曼树的当前结点指针和根结点指针
	int codelen, i, spenum;
	bool lr;

	if(fin.fail())
	{
		cout << "Cannot open the input file." << endl;
		exit(0);
	}
	if(fout.fail())
	{
		cout << "Cannot open the output file." << endl;
		exit(0);
	}

	root = h_tree.Root();
	cur = root;

	fin.read((char *)&codelen, sizeof(int));						//跳过编码文件开头的字典部分
	fin.read((char *)&spenum, sizeof(int));
	fin.seekg(5*spenum, ios::cur);

	cout << "code size: " << codelen << endl;
	for(i = 1; i <= codelen; i++)
	{
		if(cur->data.first != '#')									//找到该编码所对应的字符
		{
			fout << cur->data.first;								//将字符写入文本文件
			cur = root;												//当前指针复位
		}
		else
		{
			lr = readBit(fin);										//否则读取下一位
			if(lr)													//根据编码将指针移动到当前结点的左/右孩子结点上
				cur = cur->rchild;
			else
				cur = cur->lchild;
		}
	}

	fin.close();
	fout.close();
	cout << "Decoding finished" << endl;
	return;
}